"""
sequence of events
1. Production planning: consumption and capital firms compute their desired output level.

"""
import random
import numpy as np
from typing import List

import pandas as pd

from tqdm import tqdm

from utils import ParaPro, apt, apt_list, prt, p, INI_YEAR, cfg, TransactionFlowItem, EpisodeItem, TransactionFlowAgentItem

from agent import Firms, HouseHolds, Government, CentralBank, Banks

from contract import ConsumptionContract
from contract import CapitalContract
from contract import BondContract
from contract import CashAdvanceContract

from market import CreditMarkets
from trade import trade_from_deposit_to_deposit
from trade import trade_from_reserve_to_deposit
from trade import trade_from_treasury_deposit_to_reserve
from trade import trade_from_treasury_deposit_to_central_bank
from trade import trade_from_reserve_to_central_bank
from trade import trade_from_treasury_deposit_to_deposit
from trade import trade_from_deposit_to_treasury_deposit
from trade import trade_from_reserve_to_treasury_deposit
from trade import trade_from_central_bank_to_treasury_deposit
from trade import trade_from_central_bank_to_reserve # 央行向银行借钱
from trade import trade_from_deposit_to_reserve
from trade import trade_consumption_goods
from trade import trade_capital_goods
from trade import trade_from_ca_to_ka
from trade import trade_from_ka_to_ca


class Events:
    def __init__(
        self,
        government: Government,
        central_bank: CentralBank,
        credit_markets: CreditMarkets,
        year,
        ):
        self.year = year
        self.gov: Government = government
        self.cb: CentralBank = central_bank
        # 信贷市场 包含就业
        self.credit_market: CreditMarkets = credit_markets
        self.tf_item = TransactionFlowItem()
        self.ep_item = EpisodeItem()

    def monitor_consumption(self):
        all_contracts = {}
        for c in self.credit_market.c_firms:
            c_contracts = {h_id: c for h_id, c in c.consumption_goods_contracts_collection.items()}
            all_contracts = {**c_contracts, **all_contracts}
        total_expected_demand = np.sum([c.quantity * c.price for _, c in all_contracts.items()])
        return all_contracts, total_expected_demand

    # 1 production planning 制定生产计划 # 2 labor_demand 劳动力需求 # 3.1 pricing 定价
    def production_planning(self):
        """
        对于c和k公司，制定生产计划，生产计划制定与预期销量，存货持有意愿与期初存货情况有关。
        对于c和k公司，根据最新的生产计划（1），预计需要的工人（居民部门），不同部门计算方式不一样，
        对于k公司来说，就是y与劳动生产率决定的，对于c公司还要考虑资本存量
        """
        apt(f">>>> {self.year}【1】制定生产计划，消费品公司调用output_decision方法，资本品公司调用output_decision方法")
        apt(f">>>> {self.year}【2】公司评估劳动力需求，消费品公司调用needed_works方法，资本品公司调用needed_works")
        apt(f">>>> 【3】公司进行定价，消费品公司调用pricing方法，资本品公司调用pricing方法")
        for firms in [self.credit_market.c_firms, self.credit_market.k_firms]:
            for f in firms:
                f.produce_plan_pricing_and_cal_need_workers()

    def firm_adjust_employee_for_produce_plan(self):
        self.credit_market.firm_adjust_employee()
        self.credit_market.firm_employee_turnover()

    # 3.2 bank set_interests_of_loans_and_deposits 银行设定贷款利率和存款利率
    def set_interests_of_loans_and_deposits(self):
        """
        银行设定贷款利率和存款利率。
        :return:
        """
        apt(f">>>> 【3.2】银行设置贷款和存款利率，\n"
            f"   > 银行设置存款利率，跟自身的liquidity_ratio有关，如果超过target，说明银行存款准备金过多，降低存款利率\n"
            f"   > 银行如何设置贷款利率，跟自身的capital_ratio有关，如果超过target，说明净值/贷款过高，贷款利率要下行")
        for b in self.credit_market.banks:
            b.set_loan_interest_rate()
            b.set_deposit_interest_rate()

    # 3.3 works ask their wages 工人提出新的工资需求
    def works_ask_wages(self, unemployment_rate):
        """
        :param unemployment_rate: 工人根据失业率情况来确定自己的工资要求
        :return:
        """
        apt(f">>>> 【3.3】工人调整自己的工资，通过household.update_wages_contract方法")
        for h in self.credit_market.households:
            new_wages = h.update_wages_contract(unemployment_rate)
            h.job_contract.wage = new_wages

    # 4 desired rate of capacity growth, real demand for capital goods 企业设定对投资品的需求
    # 5 Capital good market (1): consumption firms choose their capital supplier 资本品市场，消费品公司选择资本品供应商
    def update_capital_supplier(self):
        apt(f">>>> 【5】消费品生产商切换资本品供应商, 通过self.capital_goods_market.update_k_supplier()实现")
        self.credit_market.update_k_supplier()
        # 形成合同，合同包含交易双方id，交易数量，价格，后续确定转账情况

    def update_consumption_supplier(self):
        apt(f">>>> 【11】居民消费者切换消费品供应商")
        self.credit_market.update_c_supplier()

    # 6 Credit demand（Credit Market) 信贷市场，公司选择贷款银行
    # 7 Credit supply 信贷市场，银行就公司信贷需求签合同
    def credit_supply(self):
        apt(f">>>> 【7】根据【6】产生的结果，匹配需求，签订信贷信贷合同")
        self.credit_market.credit_supply()
        # 此时还没有生产，但是需要计算很多预期变量，比如说未来的存货变动，未来的利润、未来的自由现金流等
        # credit_supply 触发finance 触发 expected_dividends and expected_ocf
        # expected_dividends 触发 expected_profits ,触发expected_sales expected_interests
        # expected_of 触发 存货变动计算，贷款本金偿还

    # 8 Labor market for unemployed 劳动力市场，签合同
    def unemployed_workers_interact_with_firms(self, unemployment_rate):
        apt(f">>>> 【8】就业市场签订劳动合同")
        for h in self.credit_market.households:
            if not h.job_contract.employed[-1]:
                h.update_wages_contract(unemployment_rate)

    # 9 Production 生产过程 劳动力市场，支付工资
    def production(self):
        apt(f">>>> 【9】C和K公司开始生产")
        for c_firm in self.credit_market.c_firms:
            c_firm.production()
        for k_firm in self.credit_market.k_firms:
            k_firm.production()


    def execute_unsettled_consumption(self, id_: str,  firms_with_inventory_left: List[str]):
        idx_all = list(range(len(firms_with_inventory_left))) 
        random.shuffle(idx_all)
        # 为什么要这么设计，因为剩余的有库存的还会想在本期继续卖货，剩余的没有库存的会想在本期买货
        for idx in idx_all:
            firm_id = firms_with_inventory_left[idx]
            c = self.credit_market.c_firms_map[firm_id]
            h = self.credit_market.households_map[id_]
            # 因为之前的合约不能满足，h消费要考虑新的价格，所以
            a1, a2, max_q, q = h.consume(c.inventory.price)
            
            if c.inventory.quantity >= q:
                contract = ConsumptionContract(
                                {
                                'c_firm_id': c.firm_id,
                                'household_id': id_,
                                'quantity': q,
                                'price': c.inventory.price,
                                'con_deposit': h.deposits_contracts.amount,
                                'con_income': h.job_contract.wage if h.job_contract.employed[-1] else h.job_contract.dole,
                                'a1':a1,
                                'a2':a2,
                                'max_q':max_q,
                                }
                            )
                # 原来未被满足的客户关系要解约
                old_firm = self.credit_market.c_firms_map[h.consumption_contract.c_firm_id]
                del old_firm.consumption_goods_contracts_collection[contract.household_id]
                # 新的关系建立
                c.consumption_goods_contracts_collection[contract.household_id] = contract
                h.consumption_contract = contract # 这里是 buyer_obj.consumption_contract = contract

                tran_f_item = self.tf_item.Consumption
                goods_episode_item = self.ep_item.goods_flow_consumption
                cash_flow_episode_item = self.ep_item.cash_flow_consumption
                
                trade_consumption_goods(
                    quantity=contract.quantity,
                    payer=h,
                    payee=c,
                    item=goods_episode_item,
                    transaction_flow_item=tran_f_item
                    )
                
                nominal_payment = contract.quantity * contract.price

                trade_from_deposit_to_deposit(
                    amount=nominal_payment,
                    payer=h,
                    payee=c,
                    payer_bank=self.credit_market.banks_map[h.deposits_contracts.bank_id],
                    payee_bank=self.credit_market.banks_map[h.deposits_contracts.bank_id],
                    item=cash_flow_episode_item,
                    transaction_flow_item=tran_f_item
                    )
                return True
    
        return False


    # 11.1 解决剩余的合同(可以是资本品，也可以是消费品)
    def execute_unsettled_capital_contract(self, id_: str, quantity: float, firms_with_inventory_left: List[str]):
        """
        执行未完成的订单，并且签订新的合约
        id_ : 买方id，可能是household，也可能是c_firm
        quantity : 订单量
        firms_with_inventory_left: 剩余有库存的公司的id
        """
        idx_all = list(range(len(firms_with_inventory_left))) 
        random.shuffle(idx_all)
        # 为什么要这么设计，因为剩余的有库存的还会想在本期继续卖货，剩余的没有库存的会想在本期买货
        for idx in idx_all:
            firm_id = firms_with_inventory_left[idx]
            firm = self.credit_market.firms_map[firm_id]
            # 公司库存大于订单量才可以继续
            if firm.inventory.quantity > quantity:
                # 如果是资本品合同，一个是k_firm_id（卖方要换）,一个是c_firm_id(不用变）
                c = self.credit_market.c_firms_map[id_]
                contract = CapitalContract(
                    {
                    'k_firm_id': firm.firm_id,
                    'c_firm_id': c.id_(),
                    'quantity': quantity,
                    'price': firm.inventory.price
                    }
                    )
                firm.capital_goods_contracts_collection[contract.c_firm_id] = contract
                # 这里buyer_obj是消费品公司，其有多种设备类型
                c.capital_goods_contracts_collection[contract.k_firm_id] = contract
                tran_f_item = self.tf_item.Investments
                goods_episode_item = self.ep_item.goods_flow_capital
                cash_flow_episode_item = self.ep_item.cash_flow_capital
                trade_capital_goods(
                    quantity=contract.quantity, 
                    payer=c, 
                    payee=firm,
                    item=goods_episode_item,
                    transaction_flow_item=tran_f_item
                    )

                nominal_payment = contract.quantity * contract.price
                assert nominal_payment > 0

                trade_from_deposit_to_deposit(
                    amount=nominal_payment,
                    payer=c,
                    payee=firm,
                    payer_bank=self.credit_market.banks_map[c.deposits_contracts.bank_id],
                    payee_bank=self.credit_market.banks_map[firm.deposits_contracts.bank_id],
                    item=cash_flow_episode_item,
                    transaction_flow_item=tran_f_item
                    )
                # 这里一定要return 否则这个合同就会执行多次，如果是消费者，它会匹配很多家C公司，导致存款快速用完
                return True
        # 如果遍历所有的卖家都没有库存能够满足，要提醒的
        return False

    # 10 capital goods market 2
    def execute_capital_goods_contract(self):
        """
        执行资本品合同，即K公司交付资本品K给C公司，并记录交易流。
        若有未完成的合同，将其重新匹配并处理。
        :return: None
        """
        # 打印提示信息，记录交易流为投资
        apt(f">>>> episode【10】,year:{self.year}K公司交付其资本品K给C公司，TransactionFlow记录为{self.tf_item.Investments},未完成的重新匹配")
        # 初始化一个空列表，用于存储所有资本品合同的信息
        print_li = []
        li = []
        k_inv_li = []
        # 遍历所有K公司
        for k in self.credit_market.k_firms:
            # 遍历每个K公司的资本品合同集合
            k_order_sum = sum([contract.quantity for _, contract in k.capital_goods_contracts_collection.items()])
            k_inv_sum = k.inventory.quantity
            if k_inv_sum < k_order_sum:
                print_li.append(f"K公司{k.firm_id}总订单量为{k_order_sum:.4f},总库存为{k_inv_sum:.4f}")
            for c_id, contract in k.capital_goods_contracts_collection.items():
                # 将合同信息添加到列表中
                li.append(contract.__dict__)
            k_inv_li.append(k.inventory.quantity)
        # 检查K合约数量是否超过C公司数量，若超过则提示K合约有问题
        sum_order = pd.DataFrame(li)['quantity'].sum()
        sum_inv = sum(k_inv_li)
        apt(f">>>> episode【10】K公司的总订单量为{sum_order:.4f},总库存为{sum_inv:.4f}")
        if len(print_li) > 0:
            apt(print_li[-1], f"一共有记录：{len(print_li)}个，选取最后一个打印")
        # 初始化一个空字典，用于存储未解决的合同
        contract_unsolved = {}
        k_firms_with_inventory_left = []

        prt_texts = []

        # 遍历所有K公司
        for k_firm in self.credit_market.k_firms:
            # 检查K公司是否有资本品合同
            if len(k_firm.capital_goods_contracts_collection) > 0:  # 一个K公司有若干C公司用户
                # 遍历K公司的每个资本品合同
                for c_id, capital_goods_contract in k_firm.capital_goods_contracts_collection.items():
                    # 根据C公司ID查找对应的C公司
                    c_firm = self.credit_market.c_firms_map[c_id]
                    # 检查K公司的库存是否足够满足合同需求
                    if k_firm.inventory.quantity >= capital_goods_contract.quantity:  # 如果库存够
                        # 确定交易数量为合同规定的数量
                        trade_quantity = capital_goods_contract.quantity
                    else:  # 如果库存不够了
                        trade_quantity = k_firm.inventory.quantity # 确定交易数量为K公司的库存数量
                        # 计算未完成的合同数量
                        unsettled_quantity = capital_goods_contract.quantity - trade_quantity  # 原始合同未完成的量
                        # 将未完成的合同数量添加到未解决合同字典中
                        contract_unsolved[c_id] = unsettled_quantity  # 所有未满足的订单放在一起
                        # 更新合同数量为能够履约的数量
                        if trade_quantity > 0:
                            capital_goods_contract.quantity = trade_quantity  # 原始的合同改成能够履约的数量

                        prt_texts.append(f">>>> {self.year}>>>【10】K库存不足，为{k_firm.inventory.quantity},合同数量为{capital_goods_contract.quantity}")

                    # 这里trade quantity 为零完全是合理的

                    # 执行资本品交易，记录商品流
                    trade_capital_goods(
                        quantity=trade_quantity,
                        item=self.ep_item.goods_flow_capital,
                        payer=c_firm,
                        payee=k_firm,
                        transaction_flow_item=self.tf_item.Investments
                    )
                    # 执行存款到存款的交易，记录现金流
                    trade_from_deposit_to_deposit(
                        amount=trade_quantity * capital_goods_contract.price,
                        payer=c_firm,
                        payee=k_firm,
                        payer_bank=self.credit_market.banks_map[c_firm.deposits_contracts.bank_id],
                        payee_bank=self.credit_market.banks_map[k_firm.deposits_contracts.bank_id],
                        item=self.ep_item.cash_flow_capital,
                        transaction_flow_item=self.tf_item.Investments,
                    )

            if k_firm.inventory.quantity > 0:
                k_firms_with_inventory_left.append(k_firm.firm_id)

        # 打印未完成的资本品合约数量
        print(f">>>> 【提示】:未完成的资本品合约数量{len(contract_unsolved)}个")
        # 遍历未解决的合同
        if len(k_firms_with_inventory_left) > 0 and len(contract_unsolved) > 0:
            for c_id, unsettled_quantity in contract_unsolved.items():
                # 尝试执行未解决的合同
                done_or_undone = self.execute_unsettled_capital_contract(
                    c_id, unsettled_quantity, k_firms_with_inventory_left
                    )
                # 若未完成，打印提示信息
                if not done_or_undone:
                    print(done_or_undone, '未完成', c_id, unsettled_quantity)

        ckn = self.credit_market.net_work_from_c_to_k('state').sum().sum()
        kcn = self.credit_market.net_work_from_k_to_c('state').sum().sum()
        if ckn != kcn:
            print('c to k network', ckn, kcn)

        apt(">>>> 【提示】capital goods market Done!!!")

    # 11. Consumption goods market
    def execute_consume(self):
        apt(f">>>> 【11】消费品公司交付消费品给消费者，存款增加，存货减少，消费者（居民）得到消费品，存款减少"
            f"TransactionFlow记录为{self.tf_item.Consumption}")
        contract_info = []
        need_more_info = []
        order_quantity = 0
        order_amount = 0
        inv_value = np.sum([c.inventory.quantity for c in self.credit_market.c_firms])
        inv_amount = np.sum([c.inventory.quantity * c.inventory.price for c in self.credit_market.c_firms])
        
        # 计算总订单和总库存
        for c_firm in self.credit_market.c_firms:
            c_order_sum = sum([contract.quantity for _, contract in c_firm.consumption_goods_contracts_collection.items()])
            c_amount = sum([c.quantity * c.price for _, c in c_firm.consumption_goods_contracts_collection.items()])
            order_quantity += c_order_sum
            order_amount += c_amount
            c_inv_sum = c_firm.inventory.quantity
            if c_inv_sum < c_order_sum:
                need_more_info.append(f">>>> 【库存问题】C公司的总订单量为{c_order_sum},总库存为{c_inv_sum}")
            for _, contract in c_firm.consumption_goods_contracts_collection.items():
                contract_info.append(contract.__dict__)
        
        if len(need_more_info) > 0:
            print(need_more_info[-1], f"众多信息{len(need_more_info)}个，仅显示最后一条") 
        print(f">>>> 【消费品供需形势】【量】总体消费品需求{order_quantity:.2f}, 库存量{inv_value:.2f}")
        print(f">>>> 【消费品供需形势】【量价】总体消费品需求{order_amount:.2f}, 库存价值量{inv_amount:.2f}")
        print_log = []
        contract_unsolved = {}
        c_firms_with_inventory_left = []
        # 遍历居民
        for c in self.credit_market.c_firms:
            # 确保C公司的订单是大于零的
            if len(c.consumption_goods_contracts_collection) > 0:
                # 遍历合同
                for h_id, consumption_contract in c.consumption_goods_contracts_collection.items():
                    # 定位消费者
                    h = self.credit_market.households_map[h_id]
                    if consumption_contract.quantity > 15:
                        print('消费不合理')
                    if c.inventory.quantity >= consumption_contract.quantity:
                        trade_quantity = consumption_contract.quantity

                        trade_consumption_goods(
                            quantity=trade_quantity,
                            payer=h,
                            payee=c,
                            item=self.ep_item.goods_flow_consumption,
                            transaction_flow_item=self.tf_item.Consumption
                            )

                        trade_from_deposit_to_deposit(
                            amount=trade_quantity * consumption_contract.price,
                            payer=h,
                            payee=c,
                            payer_bank=self.credit_market.search_bank_by_id(h.deposits_contracts.bank_id),
                            payee_bank=self.credit_market.search_bank_by_id(c.deposits_contracts.bank_id),
                            item=self.ep_item.cash_flow_consumption,
                            transaction_flow_item=self.tf_item.Consumption,
                        )

                    else:
                        print_log.append(f">>>> 【execute consume】库存不足{c.firm_id}库存为{c.inventory.quantity:6f},合同数量为{consumption_contract.quantity:6f}")
                        contract_unsolved[h_id] = consumption_contract.quantity

                if c.inventory.quantity > 0:
                    c_firms_with_inventory_left.append(c.firm_id)

        print(f">>>> 【提示】:未完成的消费合约数量{len(contract_unsolved)}个")
        apt_list(print_log)

        contract_done = {}
        if len(c_firms_with_inventory_left) > 0 and len(contract_unsolved) > 0:
            for h_id, unsettled_quantity in contract_unsolved.items():
                done_or_undone = self.execute_unsettled_consumption(h_id, c_firms_with_inventory_left)
                if not done_or_undone:
                    print(done_or_undone, '未完成', h_id, unsettled_quantity)
                else:
                    contract_done[h_id] = unsettled_quantity
        
        print(f">>>> 【提示】:未完成的消费合约数量{len(contract_unsolved)}个,经过重新匹配，完成了{len(contract_done)}个")
        self.credit_market.check_hc_net_work()
        apt(">>>> 【提示】consumption goods market Done!!!")

    # 12. loans and interests repayment
    def loan_and_interests_repayment(self):
        """
        'bank_id', 'firm_id', 'amount', 'interest_rate', 'taken_year', 'total_year', 'remaining_year',
        'repayment_per_year'
        :return:
        """
        apt(f">>>> 【12】贷款和利息偿还,公司储蓄账户减少相应金额,存款银行减少保证金,"
            f"贷款银行收到保证金,本金偿还属于{self.tf_item.delta_loans}，利息偿还属于{self.tf_item.Loans_interest}")
        ck_pay_int_args = {'c_firm': self.ep_item.c_firm_pay_loan_int, 'k_firm': self.ep_item.k_firm_pay_loan_int}
        ck_repayment_args = {'c_firm': self.ep_item.c_firm_loan_repayment, 'k_firm': self.ep_item.k_firm_loan_repayment}
        for firm in [*self.credit_market.c_firms, *self.credit_market.k_firms]:
            for loan_contract in firm.batch_of_loan_contracts:
                # 这个是为了过滤掉刚刚贷出来的钱，不用还款，也不用还利息
                if loan_contract.left_year <= p.para.duration_or_loan:
                    loan_bank = self.credit_market.search_bank_by_id(loan_contract.bank_id)  # 贷款银行
                    deposit_bank = self.credit_market.search_bank_by_id(firm.deposits_contracts.bank_id)  # 存款银行
                    interests_payment = loan_contract.amount * loan_contract.interest_rate
                    # 公司储蓄账户减少相应金额 # 存款银行减少保证金 # 贷款银行收到保证金
                    trade_from_deposit_to_reserve(
                        amount=interests_payment, 
                        payer=firm, 
                        payer_bank=deposit_bank,
                        payee=loan_bank,
                        item=ck_pay_int_args[firm.identity],
                        transaction_flow_item=self.tf_item.Loans_interest
                        )

                    if int(loan_contract.left_year) == 1:
                        principal_payment = loan_contract.amount
                        trade_from_deposit_to_reserve(
                            amount=principal_payment, 
                            payer=firm, 
                            payer_bank=deposit_bank,
                            payee=loan_bank,
                            item=ck_repayment_args[firm.identity],
                            transaction_flow_item=self.tf_item.delta_loans
                            )

                loan_contract.left_year -= 1  # 贷款期限还有10年的，过了一年，所以还剩下9年

            # 公司要清除已经偿还的贷款
            firm.batch_of_loan_contracts.clear_maturing_loan()
        
        # 银行也要清除已经偿还的贷款
        for bank in self.credit_market.banks:
            for _, batch_of_loan_contracts in bank.loans_contracts.items():
                batch_of_loan_contracts.clear_maturing_loan()

    # 12 deposit interests payment
    def deposit_interests_payment(self):
        """
        对存款支付利息
        'bank_id', 'household_id', 'amount', 'interest_rate', 'start_year', 'contract_year', 'remaining_year',
        'interest_per_year'
        :return:
        """
        apt(f">>>> 【12】存款利息偿还,存款银行负债增加，体现为居民或企业存款资产增加，银行准备金（保证金）不变 "
            f"tf_item {self.tf_item.Dep_interest}")

        for h in self.credit_market.households:
            deposit_bank = self.credit_market.search_bank_by_id(h.deposits_contracts.bank_id)
            interests_amount = h.deposits_contracts.amount * h.deposits_contracts.interest_rate
            trade_from_reserve_to_deposit(
                amount=interests_amount,
                payer=deposit_bank,
                payee=h,
                payee_bank=deposit_bank,
                item=self.ep_item.bank_pay_dep_int_to_h,
                transaction_flow_item=self.tf_item.Dep_interest
                )
            h.record_deposits_interests(interests_amount)

        item_args = {'c_firm': self.ep_item.bank_pay_dep_int_to_c, 'k_firm': self.ep_item.bank_pay_dep_int_to_k}

        for firms in [self.credit_market.c_firms, self.credit_market.k_firms]:
            for firm in firms:
                deposit_bank = self.credit_market.search_bank_by_id(firm.deposits_contracts.bank_id)
                interests_amount = firm.deposits_contracts.amount * firm.deposits_contracts.interest_rate
                trade_from_reserve_to_deposit(
                    amount=interests_amount, 
                    payer=deposit_bank, 
                    payee=firm,
                    payee_bank=deposit_bank,
                    item=item_args[firm.identity],
                    transaction_flow_item=self.tf_item.Dep_interest
                    )

    # 12 bond interests payment
    def bond_interests_payment(self):
        """
        'bank_id', 'amount', 'interest_rate', 'issue_year', 'total_year', 'remaining_year'
        :return:
        """
        apt(f">>>> 【12】债券利息偿还，如果是还给银行，政府财政存款减少，银行保证金增加，央行负债保持不变"
            f"如果是还给央行，仅政府的财政存款减少")
        for bank in self.credit_market.banks:
            for bond_contract in bank.bonds_contracts:
                interest_amount = bond_contract.interest_rate * bond_contract.amount

                trade_from_treasury_deposit_to_reserve(
                    amount=interest_amount, 
                    payer=self.gov, 
                    payee=bank,
                    item=self.ep_item.gov_pay_int_to_bank,
                    transaction_flow_item=self.tf_item.Bonds_interest
                    )

        for cb_bond_contract in self.credit_market.central_bank.bonds_collection:
            interest_amount = cb_bond_contract.interest_rate * cb_bond_contract.amount

            trade_from_treasury_deposit_to_central_bank(
                amount=interest_amount, 
                payer=self.gov,
                payee=self.credit_market.central_bank,
                item=self.ep_item.gov_pay_int_to_cb_bank,
                transaction_flow_item=self.tf_item.Bonds_interest
                )
        self.credit_market.government.has_paid_bonds_int = True

    def cash_advance_and_interests_payment(self):
        apt(f">>>> 【12】cash_advance本息偿还，银行保证金减少{self.tf_item.Advances_interest}和{self.tf_item.delta_advances}")
        for bank in self.credit_market.banks:
            cash_advance_contract = bank.cash_advance_contracts
            
            interest_amount = bank.cash_advance_interests()

            if interest_amount > 0:
                trade_from_reserve_to_central_bank(
                    amount=interest_amount, 
                    payer=bank,
                    payee=self.credit_market.central_bank,
                    item=self.ep_item.cash_advance_int,
                    transaction_flow_item=self.tf_item.Advances_interest
                    )
            if cash_advance_contract.amount > 0:
                trade_from_reserve_to_central_bank(
                    amount=cash_advance_contract.amount, 
                    payer=bank,
                    payee=self.credit_market.central_bank,
                    item=self.ep_item.cash_advance_repayment,
                    transaction_flow_item=self.tf_item.delta_advances
                    )
                # 还完cash_advance 重置下合约
                contract_info = {
                    'bank_id':bank.bank_id,
                    'interest_rate':self.credit_market.central_bank.cash_advance_rate,
                    'amount':0,
                }
                cash_advance_contract = CashAdvanceContract(contract_info)
                bank.cash_advance_contracts = cash_advance_contract
                self.credit_market.central_bank.cash_advances_contracts[bank.bank_id] = cash_advance_contract
                

    # 13. wage and dole
    def pay_wages_and_dole(self):
        apt(f">>>> 【13】发工资和救济金{self.tf_item.Wages}对于企业来说，存款减少，居民存款增加，对于政府来说，财政存款减少，居民存款增加")
        for firms in [self.credit_market.c_firms, self.credit_market.k_firms]:
            for firm in firms:
                firm_deposit_bank = self.credit_market.search_bank_by_id(firm.deposits_contracts.bank_id)
                for h_id, job_contract in firm.job_contracts_collection.items():
                    h = self.credit_market.search_household_by_id(h_id)
                    wage = job_contract.wage

                    if firm.is_cap_firm:
                        item_str = self.ep_item.k_firm_pay_wage
                    else:
                        item_str = self.ep_item.c_firm_pay_wage

                    trade_from_deposit_to_deposit(
                        amount=wage,
                        payer=firm,
                        payee=h,
                        payer_bank=firm_deposit_bank,
                        payee_bank=self.credit_market.banks_map[h.deposits_contracts.bank_id],
                        item=item_str,
                        transaction_flow_item=self.tf_item.Wages,
                    )
        for h_id, gov_job_contract in self.gov.job_contracts_collection.items():
            h = self.credit_market.search_household_by_id(h_id)
            if h.job_contract.employed[-1]:
                item_str = self.ep_item.gov_pay_wage
                wage_amount = h.job_contract.wage
                tf_item_str = self.tf_item.Wages
            else:
                item_str = self.ep_item.gov_pay_dole
                wage_amount = h.job_contract.dole
                tf_item_str = self.tf_item.Dole

            trade_from_treasury_deposit_to_deposit(
                amount=wage_amount,
                payer=self.gov,
                payee=h,
                payee_bank=self.credit_market.search_bank_by_id(h.deposits_contracts.bank_id),
                item=item_str,
                transaction_flow_item=tf_item_str,
            )
        self.credit_market.government.has_paid_doles = True
        self.credit_market.government.has_paid_wages = True

    def revalue_inventory(self):
        """
        存货的重估
        """
        args = {'c_firm': self.ep_item.c_firm_inv_revalue, 'k_firm': self.ep_item.k_firm_inv_revalue}
        for firms in [self.credit_market.c_firms, self.credit_market.k_firms]:
            for firm in firms:
                revalue_inv = firm.nominal_variation_of_inventories()
                # 增加交易流量记录，但不会影响deposits
                trade_from_ca_to_ka(
                    amount=revalue_inv, 
                    payer=firm, 
                    payee=firm,
                    transaction_flow_item=self.tf_item.CG_on_inventories,
                    item=args[firm.identity]
                    )

    # 14. tax
    def household_pay_tax(self):
        apt(f">>>> 【{self.ep_item.household_pay_tax}】居民交税 tf_item {self.tf_item.Taxes}")
        for h in self.credit_market.households:
            if h.job_contract.employed[-1]:
                tax = h.job_contract.wage * p.ratio.tax_rate
            else:
                tax = 0
            h.yearly_info[h.year].tax = tax # 记录下居民缴了多少税
            deposit_bank = self.credit_market.search_bank_by_id(h.deposits_contracts.bank_id)
            trade_from_deposit_to_treasury_deposit(
                amount=tax,
                payer=h,
                payer_bank=deposit_bank,
                payee=self.gov,
                item=self.ep_item.household_pay_tax,
                transaction_flow_item=self.tf_item.Taxes,
            )

    def firm_pay_tax(self, c=True):
        apt(f">>>> 【14】公司交税,tf_item {self.tf_item.Taxes} ep_item {self.ep_item.k_firm_pay_tax} and {self.ep_item.c_firm_pay_tax}")
        if c:
            firms = self.credit_market.c_firms
            item_str = self.ep_item.c_firm_pay_tax
        else:
            firms = self.credit_market.k_firms
            item_str = self.ep_item.k_firm_pay_tax

        for f in firms:
            profits = f.realized_profits()
            if profits > 0:
                tax = f.realized_profits() * p.ratio.tax_rate
                bank = self.credit_market.banks_map[f.deposits_contracts.bank_id]
                trade_from_deposit_to_treasury_deposit(
                    amount=tax, 
                    payer=f, 
                    payer_bank=bank, 
                    payee=self.gov, 
                    item=item_str,
                    transaction_flow_item=self.tf_item.Taxes
                    )

    def bank_pay_tax(self):
        apt(f">>>> 【{self.ep_item.bank_pay_tax}】银行交税tf_item{self.tf_item.Taxes} ep_item")
        for bank in self.credit_market.banks:
            tax = bank.tax()
            trade_from_reserve_to_treasury_deposit(
                amount=tax,
                item=self.ep_item.bank_pay_tax,
                payer=bank,
                payee=self.gov,
                transaction_flow_item=self.tf_item.Taxes,
                )

    def gov_collect_tax(self):
        apt(f">>>> 【14】政府征税,{self.tf_item.Taxes} 居民和企业交税，通过储蓄账户减少，政府财政账户增加"
            f"银行交税，保证金减少，财政存款增加")
        # 要确保本期还没有征税
        assert not self.credit_market.government.has_collected_tax
        self.household_pay_tax()
        self.firm_pay_tax(c=True)
        self.firm_pay_tax(c=False)
        self.bank_pay_tax()
        self.credit_market.government.has_collected_tax = True # 征税后改变征税状态

    # 15 Dividends
    def bank_and_firm_pay_dividends(self):
        apt(f">>>> 【15】公司和银行发股利，{self.tf_item.Profits},公司发股利，存款减少，股东（居民）存款增加，"
            f"银行发股利，如果股东存款账户不在这一银行，其保证金减少，股东存款账户增加，如果在，保证金不变存款增加")
        ep_args = {'c_firm': self.ep_item.c_firm_pay_div, 'k_firm': self.ep_item.k_firm_pay_div}
        keep_args = {'c_firm': self.ep_item.c_firm_keep_div, 'k_firm': self.ep_item.k_firm_keep_div}
        for h in tqdm(self.credit_market.households):
            h_deposit_bank = self.credit_market.banks_map[h.deposits_contracts.bank_id]
            for obj_id, s_contract in h.stockholder_contract.items():
                if s_contract.obj_type == 'bank':
                    bank = self.credit_market.banks_map[obj_id]
                    amount_of_dividends = bank.dividends * s_contract.share
                    # 修正了，亏损也要继续ca to ka
                    keep_profits = bank.keep_profits()
                    amount_of_profits_keep = bank.keep_profits() * s_contract.share
                    # 一部分分红
                    trade_from_reserve_to_deposit(
                        amount=amount_of_dividends, 
                        payer=bank, 
                        payee=h,
                        payee_bank=self.credit_market.search_bank_by_id(h.deposits_contracts.bank_id),
                        item=self.ep_item.bank_pay_div, 
                        transaction_flow_item=self.tf_item.Profits
                        )
                    h.record_dividends(amount_of_dividends)
                    # 一部分存留收益
                    trade_from_ca_to_ka(
                        amount=amount_of_profits_keep, 
                        payer=bank, 
                        payee=bank,
                        item=self.ep_item.bank_keep_div, 
                        transaction_flow_item=self.tf_item.Profits
                        )

                else:
                    firm = self.credit_market.firms_map[obj_id]
                    realized_profits = firm.realized_profits()
                    amount_of_dividends = firm.real_dividends() * s_contract.share
                    keep_dividends = firm.keep_dividends() * s_contract.share
                    # 如果股利是正的，应该发，如果是负的，KA要弥补CA的亏损
                    if realized_profits > 0:
                        # 一部分分红
                        trade_from_deposit_to_deposit(
                            amount=amount_of_dividends, 
                            payer=firm, 
                            payee=h,
                            payer_bank=self.credit_market.search_bank_by_id(firm.deposits_contracts.bank_id),
                            payee_bank=h_deposit_bank,
                            item=ep_args[firm.identity], 
                            transaction_flow_item=self.tf_item.Profits
                            )
                        h.record_dividends(amount_of_dividends)
                        # 一部分存留收益
                        trade_from_ca_to_ka(
                            amount=keep_dividends, 
                            payer=firm, 
                            payee=firm,
                            item=keep_args[firm.identity],
                            transaction_flow_item=self.tf_item.Profits
                            )
                    else:
                        trade_from_ca_to_ka(
                            amount=realized_profits * s_contract.share, 
                            payer=firm, 
                            payee=firm,
                            item=keep_args[firm.identity],
                            transaction_flow_item=self.tf_item.Profits
                            )

    # 16. deposit market interaction
    def agent_renew_their_deposit_bank(self):
        apt(f">>>> 【16】居民重现选择其存款银行")
        # households_matching_bank_for_deposit已经变成了新的，update函数更新了它的k，v
        self.credit_market.update_household_deposits_bank()

    def central_bank_transfer_profits_to_government(self):
        profits = self.credit_market.central_bank.profits()
        trade_from_central_bank_to_treasury_deposit(
            amount=profits,
            payee=self.credit_market.government,
            payer=self.credit_market.central_bank,
            item=self.ep_item.cb_bank_transfer_profits,
            transaction_flow_item=self.tf_item.CB_profits,
        )

    # 17. Bank and Central Bank purchase newly issued bonds
    def bank_and_central_bank_purchase_bonds(self):
        apt(f">>>> 【17】银行和中央银行购买债券")
        bonds_need_issue = self.credit_market.government.gov_need_issue_bonds()
        if bonds_need_issue > 0:
            bonds_left = self.bank_buy_issue_bonds_from_gov(bonds_need_issue)
            if bonds_left > 0:
                self.cb_buy_issue_bonds_from_gov(bonds_left, self.credit_market.bonds_interest_rate)

    def bank_buy_issue_bonds_from_gov(self, bonds_amount):
        apt(f">>>> 【17】银行和购买政府发行债券")
        # 首先确定潜在的能买的银行集合与其资金量
        available_collection = {}
        for bank in self.credit_market.banks:
            available_reserves = bank.available_money_for_buy_bonds()
            if available_reserves > 0:
                available_collection[bank.bank_id] = available_reserves
        sum_money = sum([v for k, v in available_collection.items()])
        # 然后根据银行对债券的需求量和政府对债券的供给量进行匹配
        # 如果bonds amount为零，那么就有问题
        correction_v = sum_money / bonds_amount
        if sum_money > bonds_amount:  
            # 如果银行的总债券购买额度大于政府拟发行量，需要除以 correction_v使得正好能够买完，否则有银行买不到
            available_collection = {bank_id: v / correction_v for bank_id, v in available_collection.items()}
        # 最后再开始便利银行与购买金额
        for bank_id, bank_buy_bonds in available_collection.items():
            bank = self.credit_market.banks_map[bank_id]
            # 银行购买政府新发行国债，那么银行资产中债券增加，保证金存款减少
            trade_from_reserve_to_treasury_deposit(
                amount=bank_buy_bonds,
                payer=bank,
                payee=self.gov,
                item=self.ep_item.banks_buy_gov_issue_bonds,
                transaction_flow_item=self.tf_item.delta_Gov_bonds
                )
            b_contract = {
                'amount': bank_buy_bonds,
                'interest_rate': p.ir.interest_rate_of_bonds,  # ?后面债券应该利率谁来定
                'issue_year': self.year,
                'remaining_year': p.para.duration_or_loan
                }

            bond_contract = BondContract(b_contract)
            bank.bonds_contracts.append(bond_contract)
            self.gov.bonds_collection.append(bond_contract)

            bonds_amount -= bank_buy_bonds
        return bonds_amount

    def cb_buy_issue_bonds_from_gov(self, amount, interest_rate):
        """
        央行购买财政部发行的债券，对于央行来说，其资产负债表中，资产方：持有政府债权增加，负债方，财政存款增加
        对于政府来说，其资产方财政存款增加，其负债方，债券
        :param amount:
        :param interest_rate:
        :return:
        """
        apt(f">>>> 【17】中央银行和购买政府发行债券")
        bonds_detail = {'amount': amount, 'interest_rate': interest_rate,
                        'issue_year': self.year, 'remaining_year': p.para.duration_or_loan}
        b_c = BondContract(bonds_detail)
        self.cb.bonds_collection.append(b_c)
        self.gov.bonds_collection.append(b_c)
        trade_from_central_bank_to_treasury_deposit(
            amount=amount,
            payer=self.credit_market.central_bank,
            payee=self.gov,
            item=self.ep_item.cb_bank_buy_gov_issue_bonds,
            transaction_flow_item=self.tf_item.delta_Gov_bonds
            )


    # 18.2 Central Bank accommodates cash advances requests by private bank
    def bank_ask_for_cash_advance(self):  # 银行向央行短期借款 to restore the mandatory liquidity ratio
        apt(f">>>> 【18】year:{self.year}银行根据其现金情况向央行借钱cash_advance")
        for bank in self.credit_market.banks:
            need_cash_advance = bank.ask_for_cash_advance()
            if need_cash_advance > 0:
                contract_info = {
                    'bank_id':bank.bank_id,
                    'interest_rate':self.credit_market.central_bank.cash_advance_rate,
                    'amount':need_cash_advance,
                }
                cash_advance_contract = CashAdvanceContract(contract_info)
                bank.cash_advance_contracts = cash_advance_contract
                self.credit_market.central_bank.cash_advances_contracts[bank.bank_id] = cash_advance_contract
                trade_from_central_bank_to_reserve(
                    item=self.ep_item.bank_borrow_cash_advance,
                    payee=bank,
                    transaction_flow_item=self.tf_item.delta_reserves,
                    amount=need_cash_advance,
                    )
                # bank.cash_advance = need_cash_advance # 重写

    def update_state_of_agents_at_episode_start(self):
        apt(f">>>> 【开始episode之前的状态更新】Agent清空其log，存货状态刷新")
        self.credit_market.reset_state()
        for c in [*self.credit_market.c_firms, *self.credit_market.k_firms]:
            c.reset_state(save_log=False)
            c.inventory.keep_quantity = c.inventory.quantity  # 期初库存更新

        for h in self.credit_market.households:
            h.reset_state()
        for b in self.credit_market.banks:
            b.reset_state()
        
        self.gov.reset_state()
        self.credit_market.central_bank.reset_state()

    def agent_end_update(self):
        apt(f">>>> 【结束episode的状态更新】")
        # 结束的时候央行记录下自己的资产负债表情况
        self.credit_market.central_bank.end_state_update()
        
        for c in self.credit_market.c_firms:
            c.end_state_update()
            c.execute_capital_amortization(execute=True)
            c.real_ocf()
        for k in self.credit_market.k_firms:
            k.end_state_update()

    def monitor_job(self):
        v_sum = 0
        for groups in [self.credit_market.c_firms, self.credit_market.k_firms]:
            n = 0
            for boj_ in groups:
                n += len(boj_.job_contracts_collection)
            v_sum += n
            print(f"所有{groups[0].identity}公司{n}个员工")
        print(f'公司一共{v_sum}个工作')
        employed_in_gov = [1 for _, job_c in self.gov.job_contracts_collection.items() if job_c.employed[-1]]
        un_employed_in_gov = [1 for _, job_c in self.gov.job_contracts_collection.items() if not job_c.employed[-1]]
        print(f"政府雇佣{len(employed_in_gov)}, 政府救济{len(un_employed_in_gov)}")    
        un_employed = [1 for h in self.credit_market.households if not h.job_contract.employed[-1]]
        print(f"失业{len(un_employed)}失业率{self.credit_market.cal_unemployment_rate():.4f}")

    def monitor_order(self):
        """
        监测C公司的订单
        """
        data_dic = {}
        c_zero = self.credit_market.c_firms[0]
        years = list(c_zero.yearly_info.keys())
        for year in years[1:-1]:
            dic = {}
            for c in self.credit_market.c_firms:
                dic[c.firm_id] = c.yearly_info[year].sales_this_period_amount_at_end_state_update
            data_dic[year] = dic
        df = pd.DataFrame(data_dic)
        return df


    def run_a_episode(self):
        prt(f">>>> episode Start 今年是{self.year}!")
        self.update_state_of_agents_at_episode_start()
        # 1. Production planning: consumption and capital firms compute their desired output level.
        # 2. Firms' labor demand: firms evaluate the number of workers needed to produce. 公司劳动力需求
        # 3. Prices, interest, and Wages: consumption and capital firms set the price of their output;
        self.production_planning()
        self.monitor_job()
        self.firm_adjust_employee_for_produce_plan()
        self.monitor_job()
        #  banks determine the interest rate on loans and deposits. Workers adaptively revise their reservation wages.
        self.set_interests_of_loans_and_deposits()
        # 居民部门设定自身需求的工资
        self.works_ask_wages(self.credit_market.cal_unemployment_rate())
        # 4. Investment in capital accumulation: consumption firms' determine their desired rate of capacity growth and,
        #    as a consequence, their real demand for capital goods.
        # 5. Capital good market (1): consumption firms choose their capital supplier.

        self.update_capital_supplier()

        # 6. Credit demand: Firms assess their demand for credit and select the lending bank.
        # 7. Credit supply: Banks evaluate loan requests and supply credit accordingly.
        self.credit_supply()
        # 8. Labor market: unemployed workers interact with firms on the labor market.
        self.unemployed_workers_interact_with_firms(self.credit_market.cal_unemployment_rate())  # 后期要改
        # 9. Production: capital and consumption firms produce their output.
        self.production()
        # 10. Capital goods market (2): consumption firms purchase capital from their supplier.
        #     New machines are employed in the production process starting from the next period.
        self.execute_capital_goods_contract()
        # 11. Consumption goods market: households interact with consumption firms and consume.
        self.update_consumption_supplier()
        # all_contracts, total_expected_demands_before = self.monitor_consumption()
        self.execute_consume()
        # all_contracts, total_expected_demands_after = self.monitor_consumption()
        # df = pd.DataFrame({h_id:c.__dict__ for h_id, c in all_contracts.items()})
        # 12. Interest, bonds and loans repayment: firms pay interests on loans and repay a (constant) share of each
        #     loan principal. The government repays bonds and interest to bonds' holders. Banks pay interest on deposits
        #     Cash advances and related interests, when present, are repaid.
        self.revalue_inventory()
        self.loan_and_interests_repayment()
        self.deposit_interests_payment()

        self.bond_interests_payment()
        self.cash_advance_and_interests_payment()
        # 13. Wages and dole: wages are paid. Unemployed workers receive a dole from the government.
        self.pay_wages_and_dole()
        # 14. Taxes: taxes on profits and income are paid to the government.
        self.gov_collect_tax()
        # 15. Dividends: dividends are distributed to households.
        self.bank_and_firm_pay_dividends()
        # 16. Deposit market interaction: households and firms select their deposit bank.
        self.agent_renew_their_deposit_bank()
        self.central_bank_transfer_profits_to_government()
        # 17. Bond purchases: banks and the Central Bank purchase newly issued bonds.
        self.bank_and_central_bank_purchase_bonds()
        # 18. Cash Advances: the Central Bank accommodates cash advances requests by private banks.
        self.bank_ask_for_cash_advance()
        
        self.agent_end_update()
        prt("episode Done!")
